#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
# Copyright 2007 Google Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

import wsgiref.handlers
from google.appengine.ext import webapp

import re
import time
import datetime
import urllib
import logging
from google.appengine.ext import db

import sys
stdin = sys.stdin
stdout = sys.stdout
reload(sys)
#sys.setdefaultencoding('utf-8')
sys.stdin = stdin
sys.stdout = stdout

class StrokeProperties(db.Model):
  stroke_id = db.StringProperty()
  canvas_id = db.StringProperty()
  user_id = db.StringProperty()
  is_append = db.BooleanProperty()
  client_time = db.IntegerProperty(default=0)
  server_time = db.IntegerProperty(default=0)
  layer = db.IntegerProperty()
  rawtext = db.TextProperty()

  def parse_text(self, text):
    tm = re.match(r"(.+ )points: ", text)
    if tm:
      props = tm.group(1)
    else:
      props = text
    tm = re.match(r"([^\s]+@\d{17}): ", props)
    if tm:
      self.canvas_id = tm.group(1)
    tm = re.search(r" user_name:(.+?) ", props)
    if tm: self.user_id = tm.group(1)
    if re.search(r" append: ", props):
      self.is_append = True
    elif re.search(r" delete: ", props):
      self.is_append = False
    tm = re.search(r" client_time:(\d+) ", props)
    if tm: self.client_time = int(tm.group(1))
    self.server_time = int(time.time() * 1000)
    tm = re.search(r" layer:(\d+) ", props)
    if tm: self.layer = int(tm.group(1))

    self.stroke_id = self.user_id + "@" + str(self.client_time)
    text = re.sub(r" server_time:(\d+) ",
                  " server_time:" + str(self.server_time) + " ",
                  text)
    self.rawtext = text

class CanvasProperties(db.Model):
  canvas_id = db.StringProperty(required=True)
  owner_nick = db.StringProperty(required=True)
  last_time = db.DateTimeProperty(auto_now=True)

def update_canvas_properties(stroke):
  query = CanvasProperties.all()
  query.filter('canvas_id = ', stroke.canvas_id)
  canvas = query.get()
  if canvas:
    canvas.last_time = datetime.datetime.now()
    canvas.put()
  else:
    m = re.match(r"(.+)@\d{17}$", stroke.canvas_id)
    if m:
      nickname = m.group(1)
    else:
      nickname = ""
    ncanvas = CanvasProperties(canvas_id=stroke.canvas_id,
                               owner_nick=nickname,
                               last_time=datetime.datetime.now())
    ncanvas.put()

def strokes_after_the_previous(stroke):

  pquery = StrokeProperties.all()
  pquery.filter('canvas_id = ', stroke.canvas_id)
  pquery.filter('user_id = ', stroke.user_id)
  pquery.filter('is_append = ', True)
  pquery.order('-server_time')
  prevs = pquery.fetch(2)
  prev_stroke = prevs[0]
  if (prev_stroke and
      prev_stroke.server_time == stroke.server_time and
      len(prevs) >= 2):
    prev_stroke = prevs[1]
  if prev_stroke:
    search_from = prev_stroke.server_time
  else:
    search_from = stroke.server_time
  query = StrokeProperties.all()
  query.filter('canvas_id = ', stroke.canvas_id)
  query.filter('server_time >= ', search_from)
  query.order('server_time')
  strlist = []
  for s in query:
    if s.is_append:
      strlist.append(s.rawtext)
      logging.info("strokes_after... " + s.rawtext)
    else:
      if s.user_id != stroke.user_id:
        logging.info("strokes_after... delete: ")
        return [str(stroke.canvas_id) + ": delete: "]
  return strlist

def stroke_is_duplicated(stroke):
  query = StrokeProperties.all()
  query.filter('user_id = ', stroke.user_id)
  query.filter('client_time = ', stroke.client_time)
  query.filter('is_append = ', stroke.is_append)
  return query.get()

def exec_append(text):
  stroke = StrokeProperties()
  stroke.parse_text(text)
  if stroke_is_duplicated(stroke):
    logging.info("append dup.")
    return []
  stroke.put()
  update_canvas_properties(stroke)
  return strokes_after_the_previous(stroke)

def exec_delete(text):
  dstroke = StrokeProperties()
  dstroke.parse_text(text)
  if stroke_is_duplicated(dstroke):
    logging.info("delete dup.")
    return []
  dstroke.put()
  query = StrokeProperties.all()
  query.filter('stroke_id = ', dstroke.stroke_id)
  query.filter('is_append = ', True)
  query.order('-server_time')
  astroke = query.get()
  if (astroke and astroke.canvas_id == dstroke.canvas_id):
    astroke.delete()
    update_canvas_properties(astroke)
  return []

def exec_get_strokes(text):
  tm = re.match(r"([^\s]+@\d{17}): ", text)
  strlist = []
  if tm:
    canvas_id = tm.group(1)
    query = StrokeProperties.all()
    query.filter('canvas_id = ', canvas_id)
    query.filter('is_append = ', True).order('server_time')
    for stroke in query:
      strlist.append(stroke.rawtext)
  logging.info("get_strokes: num of strokes: " + str(len(strlist)))
  return strlist

def exec_search_canvas_list(text):
  canvases = []
  tm = re.search(" nickname:([^\s]+)", text)
  if tm:
    query = CanvasProperties.all()
    query.filter(u"owner_nick = ", tm.group(1))
    query.order("-last_time")
    for c in query:
      canvases.append(str(c.canvas_id))
  return canvases

class MainHandler(webapp.RequestHandler):

  def get(self):
    text = urllib.unquote_plus(self.request.query_string)
    self.parse_text(text)

  def post(self):
    self.parse_text(self.request.body)

  def parse_text(self, text):
    text = unicode(text)
    logging.info("input = " + text)
    m = re.match(r"[^\s]+\s+([^\s]+?): ", text)
    strlist = [u"Hello. ", u"This is Server."]
    if m:
      cmd = m.group(1)
      logging.info("cmd = " + cmd)
      if cmd == "append":
        strlist = exec_append(text)
      elif cmd == "delete":
        strlist = exec_delete(text)
      elif cmd == "get_strokes":
        strlist = exec_get_strokes(text)
      elif cmd == "search_canvas_list":
        strlist = exec_search_canvas_list(text)
    self.response.headers['Content-Type'] = "text/plain"
    for line in strlist:
      self.response.out.write(line + "\n")

def main():
  application = webapp.WSGIApplication([('/', MainHandler)],debug=True)
  wsgiref.handlers.CGIHandler().run(application)


if __name__ == '__main__':
  main()

